﻿//圆形Image扩展组件，支持圆形碰撞检测

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace CGF
{
    public enum ImageProEffect
    {
        None,
        FlowLight,
        Gray
    }

    [AddComponentMenu("CGFramework/ImagePro")]
    public class ImagePro : Image
    {
        public float cornerRadius;
        public int cornerDetail = 10;
        public float textureScale = 1;
        public float borderWidth;
        public Color borderColor = Color.white;
        public bool gradient;
        public bool isVerticalGradient = true;
        public ImageProEffect effect = ImageProEffect.None;
        public Color32 beginColor = Color.yellow;
        public Color32 endColor = Color.red;
        public Texture flowLightTexture;
        public float flowLightLightScale = 2.5f;
        public float flowLightIntensity = 1;
        public float flowLightSpeed = 0.2f;
        public float flowLightAngle = 0.5f;

        private List<UIVertex> vertexs = new List<UIVertex>();
        private Material grayMaterial, flowLightMaterial;

        private void Update()
        {
            switch (effect)
            {
                case ImageProEffect.None:
                    if (material != null)
                    {
                        material = null;
                    }

                    break;
                case ImageProEffect.FlowLight:
                    if (material != flowLightMaterial)
                    {
                        if (!flowLightMaterial)
                        {
                            Shader shader;
                            if (Application.isEditor || !CGFConfig.Instance.UseAB)
                            {
                                shader = Shader.Find("CGFramework/FlowLight");
                                if (shader == null)
                                {
                                    Debug.Log("Shader文件不存在或显卡不支持[CGFramework/FlowLight]!");
                                    return;
                                }

                                flowLightMaterial = new Material(shader);
                            }
                            else if (CGFConfig.Instance.ResStrategy == EResStrategy.Resources)
                            {
                                shader = Resources.Load<Shader>(Define.ShaderFolder + "/FlowLight");
                                flowLightMaterial = new Material(shader);
                            }
                            else
                            {
                                SetMaterialWithShader(flowLightMaterial, "FlowLight");
                            }
                        }

                        material = flowLightMaterial;
                    }

                    if (material.HasProperty("_LightTex") && material.GetTexture("_LightTex") != flowLightTexture)
                    {
                        material.SetTexture("_LightTex", flowLightTexture);
                    }

                    if (material.GetFloat("_LightScale") != flowLightLightScale)
                    {
                        material.SetFloat("_LightScale", flowLightLightScale);
                    }

                    if (material.GetFloat("_Intensity") != flowLightIntensity)
                    {
                        material.SetFloat("_Intensity", flowLightIntensity);
                    }

                    if (material.GetFloat("_Speed") != flowLightSpeed)
                    {
                        material.SetFloat("_Speed", flowLightSpeed);
                    }

                    if (material.GetFloat("_Angle") != flowLightAngle)
                    {
                        material.SetFloat("_Angle", flowLightAngle);
                    }

                    break;
                case ImageProEffect.Gray:
                    if (material != grayMaterial)
                    {
                        if (!grayMaterial)
                        {
                            Shader shader;
                            if (Application.isEditor || !CGFConfig.Instance.UseAB)
                            {
                                shader = Shader.Find("CGFramework/UGUIGray");
                                grayMaterial = new Material(shader);
                            }
                            else if (CGFConfig.Instance.ResStrategy == EResStrategy.Resources)
                            {
                                shader = Resources.Load<Shader>(Define.ShaderFolder + "/UGUIGray");
                                grayMaterial = new Material(shader);
                            }
                            else
                            {
                                SetMaterialWithShader(grayMaterial, "UGUIGray");
                            }
                        }

                        material = grayMaterial;
                    }

                    break;
            }
        }

        private async void SetMaterialWithShader(Material flowLightMaterial, string shaderName)
        {
            Shader shader = await ResManager.Instance.LoadAsset<Shader>($"{Define.ShaderFolder}/{shaderName}");
            if (shader != null)
            {
                flowLightMaterial = new Material(shader);
            }
        }

        protected override void OnPopulateMesh(VertexHelper vh)
        {
            if (!IsActive())
            {
                return;
            }

            vh.Clear();

            Vector2 size = new Vector2(rectTransform.rect.width, rectTransform.rect.height);
            float halfSizeX = size.x * 0.5f;
            float halfSizeY = size.y * 0.5f;
            float halfSizeMin = Mathf.Min(halfSizeX, halfSizeY);

            // 保存原始颜色
            Color originalColor = color;

            // 1. 先绘制描边（如果有）
            float innerCornerRadius = 0;
            if (borderWidth > 0)
            {
                float effectiveBorderWidth = Mathf.Min(borderWidth, halfSizeMin);
                float effectiveCornerRadius = Mathf.Min(cornerRadius, halfSizeMin);
                innerCornerRadius = Mathf.Max(0, effectiveCornerRadius - effectiveBorderWidth);

                DrawBorder(vh, size, effectiveBorderWidth, cornerRadius, innerCornerRadius);
            }

            // 2. 绘制内部图形
            if (cornerRadius <= 0)
            {
                //无圆角
                DrawSimpleRect(vh, size, borderWidth, originalColor);
            }
            else
            {
                //有圆角
                if (borderWidth > 0)
                {
                    //有描边
                    float effectiveCornerRadius = Mathf.Min(cornerRadius, halfSizeMin - borderWidth);
                    DrawRoundedRect(vh, size, innerCornerRadius, borderWidth, originalColor);
                }
                else
                {
                    //无描边
                    DrawRoundedRect(vh, size, cornerRadius, borderWidth, originalColor);
                }
            }

            // 3. 应用渐变色（只影响内部图形）
            if (gradient)
            {
                ApplyGradient(vh, borderWidth > 0, isVerticalGradient);
            }
        }

        private void DrawBorder(VertexHelper vh, Vector2 size, float borderWidth, float outerCornerRadius, float innerCornerRadius)
        {
            float halfSizeX = size.x * 0.5f;
            float halfSizeY = size.y * 0.5f;

            float innerHalfSizeX = halfSizeX - borderWidth;
            float innerHalfSizeY = halfSizeY - borderWidth;

            if (outerCornerRadius > 0)
            {
                DrawRoundedBorder(vh, halfSizeX, halfSizeY, innerHalfSizeX, innerHalfSizeY,
                    outerCornerRadius, innerCornerRadius, borderColor);
            }
            else
            {
                DrawSimpleBorder(vh, halfSizeX, halfSizeY, innerHalfSizeX, innerHalfSizeY, borderColor);
            }
        }

        private void DrawRoundedBorder(VertexHelper vh, float outerHalfSizeX, float outerHalfSizeY,
            float innerHalfSizeX, float innerHalfSizeY,
            float outerCornerRadius, float innerCornerRadius, Color borderColor)
        {
            float incRadian = Mathf.Deg2Rad * 360 / (cornerDetail * 4);
            int segments = (cornerDetail + 1) * 4;

            List<Vector2> outerVertices = new List<Vector2>();
            List<Vector2> innerVertices = new List<Vector2>();

            GenerateRoundedCornerVertices(outerVertices, outerHalfSizeX, outerHalfSizeY, outerCornerRadius, incRadian);
            GenerateRoundedCornerVertices(innerVertices, innerHalfSizeX, innerHalfSizeY, innerCornerRadius, incRadian);

            int startIndex = vh.currentVertCount;
            for (int i = 0; i < outerVertices.Count; i++)
            {
                AddVertex(vh, outerVertices[i], borderColor);
                AddVertex(vh, innerVertices[i], borderColor);
            }

            for (int i = 0; i < segments; i++)
            {
                int currentOuter = startIndex + i * 2;
                int nextOuter = startIndex + ((i + 1) % segments) * 2;
                int currentInner = currentOuter + 1;
                int nextInner = nextOuter + 1;

                vh.AddTriangle(currentOuter, nextOuter, currentInner);
                vh.AddTriangle(currentInner, nextOuter, nextInner);
            }
        }

        private void GenerateRoundedCornerVertices(List<Vector2> vertices, float halfSizeX, float halfSizeY, float cornerRadius, float incRadian)
        {
            // 右上角
            float centerX = halfSizeX - cornerRadius;
            float centerY = halfSizeY - cornerRadius;
            for (int i = 0; i <= cornerDetail; i++)
            {
                float r = Mathf.PI * 0.5f - incRadian * i;
                vertices.Add(new Vector2(
                    centerX + Mathf.Cos(r) * cornerRadius,
                    centerY + Mathf.Sin(r) * cornerRadius
                ));
            }

            // 右下角
            centerY = -halfSizeY + cornerRadius;
            for (int i = 0; i <= cornerDetail; i++)
            {
                float r = -incRadian * i;
                vertices.Add(new Vector2(
                    centerX + Mathf.Cos(r) * cornerRadius,
                    centerY + Mathf.Sin(r) * cornerRadius
                ));
            }

            // 左下角
            centerX = -halfSizeX + cornerRadius;
            for (int i = 0; i <= cornerDetail; i++)
            {
                float r = -Mathf.PI * 0.5f - incRadian * i;
                vertices.Add(new Vector2(
                    centerX + Mathf.Cos(r) * cornerRadius,
                    centerY + Mathf.Sin(r) * cornerRadius
                ));
            }

            // 左上角
            centerY = halfSizeY - cornerRadius;
            for (int i = 0; i <= cornerDetail; i++)
            {
                float r = -incRadian * i + Mathf.PI;
                vertices.Add(new Vector2(
                    centerX + Mathf.Cos(r) * cornerRadius,
                    centerY + Mathf.Sin(r) * cornerRadius
                ));
            }
        }

        private void DrawSimpleBorder(VertexHelper vh, float outerHalfSizeX, float outerHalfSizeY,
            float innerHalfSizeX, float innerHalfSizeY, Color borderColor)
        {
            int startIndex = vh.currentVertCount;

            AddVertex(vh, new Vector2(-outerHalfSizeX, outerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(outerHalfSizeX, outerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(outerHalfSizeX, -outerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(-outerHalfSizeX, -outerHalfSizeY), borderColor);

            AddVertex(vh, new Vector2(-innerHalfSizeX, innerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(innerHalfSizeX, innerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(innerHalfSizeX, -innerHalfSizeY), borderColor);
            AddVertex(vh, new Vector2(-innerHalfSizeX, -innerHalfSizeY), borderColor);

            for (int i = 0; i < 4; i++)
            {
                int currentOuter = startIndex + i;
                int nextOuter = startIndex + (i + 1) % 4;
                int currentInner = startIndex + 4 + i;
                int nextInner = startIndex + 4 + (i + 1) % 4;

                vh.AddTriangle(currentOuter, nextOuter, currentInner);
                vh.AddTriangle(currentInner, nextOuter, nextInner);
            }
        }

        private void DrawSimpleRect(VertexHelper vh, Vector2 size, float borderWidth, Color fillColor)
        {
            float halfSizeX = size.x * 0.5f - borderWidth;
            float halfSizeY = size.y * 0.5f - borderWidth;

            int startIndex = vh.currentVertCount;

            AddVertex(vh, new Vector2(-halfSizeX, halfSizeY), fillColor);
            AddVertex(vh, new Vector2(halfSizeX, halfSizeY), fillColor);
            AddVertex(vh, new Vector2(halfSizeX, -halfSizeY), fillColor);
            AddVertex(vh, new Vector2(-halfSizeX, -halfSizeY), fillColor);

            vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
            vh.AddTriangle(startIndex, startIndex + 2, startIndex + 3);
        }

        private void DrawRoundedRect(VertexHelper vh, Vector2 size, float cornerRadius, float borderWidth, Color fillColor)
        {
            float halfSizeX = size.x * 0.5f - borderWidth;
            float halfSizeY = size.y * 0.5f - borderWidth;

            // 限制圆角半径不超过最小尺寸
            cornerRadius = Mathf.Min(cornerRadius, Mathf.Min(halfSizeX, halfSizeY));

            // 添加中心顶点
            int centerIndex = vh.currentVertCount;
            AddVertex(vh, Vector2.zero, fillColor);

            // 生成四个圆角的顶点（顺时针顺序）
            List<Vector2> vertices = new List<Vector2>();

            // 右上圆角
            GenerateCornerVertices(vertices,
                halfSizeX - cornerRadius, halfSizeY - cornerRadius,
                cornerRadius, 90f, 0f, cornerDetail);

            // 右下圆角
            GenerateCornerVertices(vertices,
                halfSizeX - cornerRadius, -halfSizeY + cornerRadius,
                cornerRadius, 0f, -90f, cornerDetail);

            // 左下圆角
            GenerateCornerVertices(vertices,
                -halfSizeX + cornerRadius, -halfSizeY + cornerRadius,
                cornerRadius, -90f, -180f, cornerDetail);

            // 左上圆角
            GenerateCornerVertices(vertices,
                -halfSizeX + cornerRadius, halfSizeY - cornerRadius,
                cornerRadius, 180f, 90f, cornerDetail);

            // 闭合路径：添加第一个顶点作为最后一个顶点
            vertices.Add(vertices[0]);

            // 添加所有顶点到VertexHelper
            int firstVertexIndex = vh.currentVertCount;
            foreach (Vector2 vertex in vertices)
            {
                AddVertex(vh, vertex, fillColor);
            }

            // 连接三角形（扇形方式）
            for (int i = 0; i < vertices.Count - 1; i++)
            {
                vh.AddTriangle(centerIndex, firstVertexIndex + i, firstVertexIndex + i + 1);
            }
        }

        // 生成单个圆角的顶点
        private void GenerateCornerVertices(List<Vector2> vertices,
            float centerX, float centerY,
            float radius, float startAngle, float endAngle, int segments)
        {
            float angleStep = (endAngle - startAngle) / segments;

            for (int i = 0; i <= segments; i++)
            {
                float angle = startAngle + angleStep * i;
                float rad = angle * Mathf.Deg2Rad;
                vertices.Add(new Vector2(
                    centerX + Mathf.Cos(rad) * radius,
                    centerY + Mathf.Sin(rad) * radius
                ));
            }
        }

        private void ApplyGradient(VertexHelper vh, bool hasBorder, bool isVerticalGradient)
        {
            var count = vh.currentVertCount;
            if (count == 0) return;

            vertexs.Clear();
            for (var i = 0; i < count; i++)
            {
                var vertex = new UIVertex();
                vh.PopulateUIVertex(ref vertex, i);
                vertexs.Add(vertex);
            }

            // 计算渐变范围（跳过描边顶点）
            int startIndex = hasBorder ? GetBorderVertexCount() : 0;
            float min = isVerticalGradient ? vertexs[startIndex].position.y : vertexs[startIndex].position.x;
            float max = min;

            for (var i = startIndex + 1; i < count; i++)
            {
                var value = isVerticalGradient ? vertexs[i].position.y : vertexs[i].position.x;
                if (value > max) max = value;
                else if (value < min) min = value;
            }

            float range = max - min;
            if (Mathf.Approximately(range, 0)) return;

            for (var i = startIndex; i < count; i++)
            {
                var vertex = vertexs[i];
                float positionValue = isVerticalGradient ? vertex.position.y : vertex.position.x;
                float t = (positionValue - min) / range;
                // 如果是横向渐变，自动反转方向（从右到左）
                if (!isVerticalGradient)
                {
                    t = 1 - t; // 反转插值比例
                }
                vertex.color = Color32.Lerp(endColor, beginColor, t);
                vh.SetUIVertex(vertex, i);
            }
        }

        private int GetBorderVertexCount()
        {
            if (cornerRadius <= 0)
            {
                return 8; // 简单矩形描边顶点数
            }
            else
            {
                return (cornerDetail + 1) * 4 * 2; // 圆角描边顶点数
            }
        }

        private void AddVertex(VertexHelper vh, Vector2 position, Color vertexColor)
        {
            UIVertex vertex = UIVertex.simpleVert;
            vertex.color = vertexColor;
            Vector2 size = new Vector2(rectTransform.rect.width, rectTransform.rect.height);
            Vector2 pivot = rectTransform.pivot;
            vertex.position.x = position.x + size.x * (0.5f - pivot.x);
            vertex.position.y = position.y + size.y * (0.5f - pivot.y);
            Vector2 uv0 = new Vector2((position.x + size.x * 0.5f) / size.x, (position.y + size.y * 0.5f) / size.y);

            uv0 *= textureScale;
            uv0 -= Vector2.one * (textureScale - 1f) * 0.5f;

            vertex.uv0 = uv0;
            vh.AddVert(vertex);
        }

#if UNITY_EDITOR
        [UnityEditor.MenuItem("CGFramework/创建 ImagePro")]
        [UnityEditor.MenuItem("GameObject/CGFramework/创建 ImagePro", priority = -2200)]
        static void Create()
        {
            GameObject go = new GameObject("ImagePro");
            go.layer = LayerMask.NameToLayer("UI");
            go.transform.SetParent(UnityEditor.Selection.activeGameObject.transform, false);
            go.AddComponent<ImagePro>();
            UnityEditor.EditorUtility.SetDirty(go);
        }

        [UnityEditor.MenuItem("GameObject/CGFramework/Image 转 ImagePro", priority = -2100)]
        public static void ChangeToImagePro()
        {
            for (int i = 0; i < UnityEditor.Selection.objects.Length; i++)
            {
                GameObject go = UnityEditor.Selection.objects[i] as GameObject;
                Image img = go.GetComponent<Image>();
                if (img)
                {
                    Sprite sprite = img.sprite;
                    Color color = img.color;
                    Material material = img.material;
                    bool raycastTarget = img.raycastTarget;
                    Type type = img.type;
                    DestroyImmediate(img);
                    ImagePro imgPro = go.AddComponent<ImagePro>();
                    imgPro.sprite = sprite;
                    imgPro.color = color;
                    imgPro.material = material;
                    imgPro.raycastTarget = raycastTarget;
                    imgPro.type = type;
                }
            }
        }
        
        [UnityEditor.MenuItem("GameObject/CGFramework/ImagePro 转 Image", priority = -2100)]
        public static void ChangeToImage()
        {
            for (int i = 0; i < UnityEditor.Selection.objects.Length; i++)
            {
                GameObject go = UnityEditor.Selection.objects[i] as GameObject;
                ImagePro imgPro = go.GetComponent<ImagePro>();
                if (imgPro)
                {
                    Sprite sprite = imgPro.sprite;
                    Color color = imgPro.color;
                    bool raycastTarget = imgPro.raycastTarget;
                    Type type = imgPro.type;
                    DestroyImmediate(imgPro);
                    Image img = go.AddComponent<Image>();
                    img.sprite = sprite;
                    img.color = color;
                    img.material = null;
                    img.raycastTarget = raycastTarget;
                    img.type = type;
                }
            }
        }
#endif
    }
}